#include <QtGui>
#include "graphwidget.h"
#include "window.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPushButton>
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <sstream>
#include "agent.h"
#include "node.h"
#include "edge.h"
#include "semaphore.h"


using namespace std;
using namespace Traffic_Sim;

GraphWidget * Window::graphwidget=NULL;
Window * Window::instance=NULL;


Window::Window()
{
  graphwidget = new GraphWidget();
  graph = new Traffic_Graph(1);
  QVBoxLayout *layout1 = new QVBoxLayout();
  
  QHBoxLayout *layout2 = new QHBoxLayout();
  
  QVBoxLayout *layout3 = new QVBoxLayout();
  
  QVBoxLayout *layout4 = new QVBoxLayout();
  layout1->addStretch();
  
  layout1->addWidget(graphwidget);
  
  layout1->addLayout(layout2);
  
  textout = new QTextEdit();
  textout->setReadOnly(true);
  
  layout2->addWidget(textout);
  
  layout2->addLayout(layout3, 0);
  layout2->addLayout(layout4, 0);
  
  QPushButton *quit2 = new QPushButton(tr("Inicio"));
  quit2->setFont(QFont("Times", 12, QFont::Bold));
  QPushButton *quit3 = new QPushButton(tr("Fin"));
  quit3->setFont(QFont("Times", 12, QFont::Bold));
  QPushButton *quit4 = new QPushButton(tr("Suspender"));
  quit4->setFont(QFont("Times", 12, QFont::Bold));
  QPushButton *quit5 = new QPushButton(tr("Resumir"));
  quit5->setFont(QFont("Times", 12, QFont::Bold));
  
  connect(quit2, SIGNAL(clicked()), this, SLOT(start()));
  connect(quit3, SIGNAL(clicked()), this, SLOT(stop()));
  connect(quit4, SIGNAL(clicked()), this, SLOT(suspend()));
  connect(quit5, SIGNAL(clicked()), this, SLOT(resume()));
  
  quit2->setMinimumWidth(100);
  quit3->setMinimumWidth(100);
  quit4->setMinimumWidth(100);
  quit5->setMinimumWidth(100);
  
  layout3->addWidget(quit2, 0, Qt::AlignHCenter);
  layout3->addWidget(quit3, 0, Qt::AlignHCenter);
  layout4->addWidget(quit4, 0, Qt::AlignHCenter);
  layout4->addWidget(quit5, 0, Qt::AlignHCenter);
  
  setLayout(layout1);
  setWindowTitle(tr("Simulacion de Trafico"));
  instance = this;  
  configure_traffic_graph();
}



void Window::create_street(const string & src_node_desc,
			   const string & dest_node_desc,
			   int delay, int capacity,
			   const string & street_name,
			   float src_x,
			   float src_y,
			   float dest_x,
			   float dest_y)
{
  Traffic_Node_Info info1(src_node_desc,src_x,src_y);
  Traffic_Graph::Node * n1 = graph->search_node(info1);
  
  Node * gn1; 
  Node * gn2;
  
  if (n1 == NULL) 
    {
      n1 = graph->insert_node(info1);
      gn1 = graphwidget->addNode(info1);
    }else {
      gn1= graphwidget->searchNode(info1);
    }
  Traffic_Node_Info info2(dest_node_desc,dest_x,dest_y);
  Traffic_Graph::Node * n2 = graph->search_node(info2);
  if (n2 == NULL)
    {
      n2 = graph->insert_node(info2);
      gn2 = graphwidget->addNode(info2);
    } else 
      {
	gn2 = graphwidget->searchNode(info2);
      }
  
  Traffic_Arc_Info info_arc(street_name, delay, capacity);
  
  graph->insert_arc(n1, n2, info_arc); 
  graphwidget->addEdge(gn1,gn2,info_arc);  	
  graphwidget->scene->update();
  stringstream outstrm(stringstream::out) ;
  outstrm << "Creada " << street_name << " entre " << src_node_desc << " y " << dest_node_desc;
  print_stream_out(outstrm);  
}


void Window::configure_traffic_graph()
{
  //Configurados
  string * arcs_desc_reg = new string[4];
  string * arcs_desc_3 = new string[3];  
  create_street("Las Tapias", "Humboldt", 10, 20, "Albarregas", -960,50,-885,-185);
  create_street("Las Tapias", "Pie del Llano", 10, 20, "Av. Andres Bello", -960,50,-335,105);
  create_street("Humboldt", "Santo Niño", 10, 20, "Av. Humboldt",  -885,-185,-950,-320 );
  create_street("Humboldt", "Santa Barbara", 30, 20, "Las Americas 1" ,  -885,-185,-305,-135);
  create_street("Santo Niño", "Casa Blanca", 20, 20, "Panamericana", -950,-320,200,-340);
  create_street("Santa Barbara", "Mc Donalds", 20, 20, "Las Americas 2",-305,-135,180,-105);
  create_street("Pie del Llano", "Santa Barbara", 7, 20, "Sucre",-335,105,-305,-135);
  create_street("Pie del Llano", "Guillermo Soto Rosa", 5, 20, "16 de Septiembre 1",-335,105,-335,205);
  create_street("Mc Donalds", "Casa Blanca", 10, 20, "El Rodeo",180,-105,200,-340);
  create_street("Guillermo Soto Rosa", "Medicina", 10, 20, "16 de Septiembre 2",-335,205,500,245);
  create_street("Medicina", "Glorias Patrias", 4, 20, "Miranda 1",500,245,500,100);
  create_street("Pie del Llano", "Glorias Patrias", 15, 20, "Av. Urdaneta",-335,105,500,100);
  create_street("Glorias Patrias", "Mercado", 6, 20, "Miranda 2",500,100,490,-80);
  create_street("Mc Donalds", "Mercado", 7, 20, "Las Americas",180,-105,490,-80);
  create_street("Casa Blanca", "Alto Prado", 14, 20, "Los Proceres",200,-340,910,-320);
  create_street("Mercado", "Viaducto", 7, 20, "Las Americas",490,-80,1040,-100);
  create_street("Viaducto", "Alto Prado", 6, 20, "Campo Elias",1040,-100,910,-320);

  arcs_desc_3[0] = "Santo Niño";
  arcs_desc_3[1] = "Las Tapias";
  arcs_desc_3[2] = "Santa Barbara";
  create_semaphore_agent (1, "Humboldt", 3, 1, arcs_desc_3);

  arcs_desc_3[0] = "Mc Donalds";
  arcs_desc_3[1] = "Humboldt";
  arcs_desc_3[2] = "Pie del Llano";
  create_semaphore_agent (2, "Santa Barbara", 3, 2, arcs_desc_3);

  arcs_desc_reg[0] = "Las Tapias";
  arcs_desc_reg[1] = "Guillermo Soto Rosa";
  arcs_desc_reg[2] = "Glorias Patrias";
  arcs_desc_reg[3] = "Santa Barbara";
  create_semaphore_agent (3, "Pie del Llano", 4, 3, arcs_desc_reg);

  arcs_desc_3[0] = "Medicina";
  arcs_desc_3[1] = "Mercado";
  arcs_desc_3[2] = "Pie del Llano";
  create_semaphore_agent (4, "Glorias Patrias", 3, 1, arcs_desc_3);

  arcs_desc_3[0] = "Santa Barbara";
  arcs_desc_3[1] = "Mercado";
  arcs_desc_3[2] = "Casa Blanca";
  create_semaphore_agent (5, "Mc Donalds", 3, 2, arcs_desc_3);

  arcs_desc_3[0] = "Alto Prado";
  arcs_desc_3[1] = "Santo Niño";
  arcs_desc_3[2] = "Mc Donalds";
  create_semaphore_agent (6, "Casa Blanca", 3, 2, arcs_desc_3);

  arcs_desc_3[0] = "Viaducto";
  arcs_desc_3[1] = "Mc Donalds";
  arcs_desc_3[2] = "Glorias Patrias";
  create_semaphore_agent (7, "Mercado", 3, 3, arcs_desc_3);

  program_agent_creation();

}

void Window::semaphore_update(void * semaphore_ptr)
{
  Semaphore * sem1 = static_cast<Semaphore *>(semaphore_ptr); 
   QEvent * ev = new QEvent( QEvent::Paint );
     sem1->event(ev);
}

void Window::create_semaphore_agent(int id, const string & node_desc, int arc_count,
				    long activation_time, string* arc_nodes_desc )
{
  stringstream agent_name_strm(stringstream::out) ;
  agent_name_strm << "Tricolor en " << node_desc;
  Traffic_Node_Info node_info(node_desc);
  Traffic_Node * node = graph->search_node(node_info);
  Traffic_Semaphore * semaphore;
  string agent_name = agent_name_strm.str();
  if (arc_count == 3)
    {
      semaphore = new Traffic_Semaphore(id, agent_name, node, arc_count);
    } 
  else 
    {
      semaphore = new Traffic_Semaphore(id, agent_name, node,  arc_count);
    }
  node->get_info().semaphore = semaphore;
  Semaphore * sem1;
  for (int i = 0; i < graphwidget->nodeList.size(); ++i)
    {
      if (graphwidget->nodeList[i]->info.description==node_desc)
	{
	  
	  sem1 = graphwidget->nodeList[i]->addSemaphore(semaphore);
	}
    }
  for (int i = 0; i < arc_count; i++)
    {
      Traffic_Node_Info dest_node_info(arc_nodes_desc[i]);
      Traffic_Node * dest_node = graph->search_node(dest_node_info);
      Traffic_Arc * arc = graph->search_arc(node, dest_node);
      semaphore->set_arc_index(arc, i);
    }
  semaphore->set_update_callback(&semaphore_update, sem1);
  Traffic_Agent_Impl::create_agent_in_node(semaphore, graph, node, activation_time);
  stringstream outstrm(stringstream::out);
  outstrm << "Creado tricolor en " << node_desc;
  print_stream_out(outstrm);  
}


Traffic_Agent_Impl * Window::create_vehicle(long id)
{
    stringstream agent_name_strm(stringstream::out) ;
    agent_name_strm << "Agente " << id;
    string agent_name =  agent_name_strm.str();
    Traffic_Random_Vehicle *vehicle = new Traffic_Random_Vehicle(id, agent_name);
    return vehicle;
}


void Window::vehicle_situation(void * agent_impl)
{
  
  Agent * agent = static_cast <Agent *> (agent_impl);
  Traffic_Vehicle * vehicle = agent->vehicle;
  Traffic_Node * node;

  if(vehicle->get_last_event()==Agent_Destruction)
    {
      delete agent;
    }
  else
    {
      if (agent->vehicle->get_agent().in_node)
	{
	  node = static_cast<Traffic_Node *>(agent->vehicle->get_agent().location);
	}else{
	  Traffic_Arc * arc = static_cast<Traffic_Arc *>(agent->vehicle->get_agent().location);
	  node = static_cast<Traffic_Node *>(arc->src_node);}

      for (int i = 0; i < graphwidget->nodeList.size(); ++i)
	{
	  if (graphwidget->nodeList[i]->info.description==node->get_info().description)
	    {
	     graphwidget->scene->addItem(agent);
	    }
	}
   }
}

void Window::assign_vehicle_renderer(Traffic_Agent_Impl * agent_impl)
{
  Agent* agent = new Agent();
  agent->setAgent(agent_impl);
  agent->vehicle->set_update_callback(&vehicle_situation, agent);
  
}

void Window::program_agent_creation()
{
  Random_Time * rtime = new Random_Time(Exponential, 3);
  Traffic_Agent_Creation_Event * creation_event = new Traffic_Agent_Creation_Event(&create_vehicle, rtime, 10);
  creation_event->set_mode(Count_Mode);
  creation_event->set_stop_probability(10);
  creation_event->set_renderer_callback(&assign_vehicle_renderer);
  creation_event->set_next_agent_id(1);
  creation_event->set_occurrence_mean(15);
  graph->schedule_event(creation_event);
}

void Window::start()
{
  graph->start_graph();
paint = true;
}

void Window::stop()
{
  graph->stop_graph();
paint = false;
}

void Window::quit()
{
  delete graph;
  quit();
}

void Window::suspend()
{
  graph->suspend_graph();
paint = false;
}

void Window::resume()
{
  graph->resume_graph();
paint = false;
}

void Window::print_stream_out(const stringstream & stream)
{
  // Se vaciará el flujo solo por obtener la cadena desde el?
  textout->append(QString::fromStdString(stream.str()));
}
